#include "image.h"

#include <math.h>
#include <stdlib.h>

#include "extras/simplexnoise1234.h"

inline float frand() {
	return rand() / (float) RAND_MAX;
}

Image::Image(int w, int h, int d){

	data = new float[w*h*d];

	this->w = w;
	this->h = h;
	this->d = d;

}


Image::~Image(){

	delete[] data;

}

Image* Image::createGauss(){


	Image * img = new Image(5,5);

	// create basic 5x5 gauss filter
	img->set(0,0) = 1.f;img->set(0,1) = 4.f; img->set(0,2) = 6.f; img->set(0,3) = 4.f; img->set(0,4) = 1.f;
	img->set(1,0) = 4.f;img->set(1,1) = 16.f;img->set(1,2) = 24.f;img->set(1,3) = 16.f;img->set(1,4) = 4.f;
	img->set(2,0) = 6.f;img->set(2,1) = 24.f;img->set(2,2) = 36.f;img->set(2,3) = 24.f;img->set(2,4) = 6.f;
	img->set(3,0) = 4.f;img->set(3,1) = 16.f;img->set(3,2) = 24.f;img->set(3,3) = 16.f;img->set(3,4) = 4.f;
	img->set(4,0) = 1.f;img->set(4,1) = 4.f; img->set(4,2) = 6.f; img->set(4,3) = 4.f; img->set(4,4) = 1.f;


	return img;
}

// The following code is from Stefan Gustavssons GLSL Simplex Noise implementation

int perm[256]= {151,160,137,91,90,15,
  131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
  190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
  88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
  77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
  102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
  135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
  5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
  223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
  129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
  251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
  49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
  138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180};

/* These are Ken Perlin's proposed gradients for 3D noise. I kept them for
   better consistency with the reference implementation, but there is really
   no need to pad this to 16 gradients for this particular implementation.
   If only the "proper" first 12 gradients are used, they can be extracted
   from the grad4[][] array: grad3[i][j] == grad4[i*2][j], 0<=i<=11, j=0,1,2
*/
int grad3[16][3] = {{0,1,1},{0,1,-1},{0,-1,1},{0,-1,-1},
                   {1,0,1},{1,0,-1},{-1,0,1},{-1,0,-1},
                   {1,1,0},{1,-1,0},{-1,1,0},{-1,-1,0}, // 12 cube edges
                   {1,0,-1},{-1,0,-1},{0,-1,1},{0,1,1}}; // 4 more to make 16

Image* Image::createPermutationMap(){

	Image * img = new Image(256,256,4);

	for(int i = 0; i<256; i++){
		for(int j = 0; j<256; j++) {

			char value = perm[(j+perm[i]) & 0xFF];
			img->set(i,j,1) = grad3[value & 0x0F][0] * 64 + 64;   // Gradient x
			img->set(i,j,2) = grad3[value & 0x0F][1] * 64 + 64; // Gradient y
			img->set(i,j,3) = grad3[value & 0x0F][2] * 64 + 64; // Gradient z
			img->set(i,j,4) = value;                     // Permuted index
		}
	}
	return img;
}

// ***********************************************************************************

Image* Image::createHeightMap(int w, int h, int res){

	Image * img = new Image(w*res, h*res);


	for(int x=0;x<w*res;++x){
		for(int y=0;y<h*res;++y){

			img->set(x,y) = SimplexNoise1234::noise(x*.01f/res,y*.01f/res)
							*fabs(SimplexNoise1234::noise(x*.003f/res + .4f,y*.003f/res + .4f))*.5f
							+SimplexNoise1234::noise(x*.2f/res,y*.2f/res)*.01f;

		}
	}

	return img;

}


void Image::convolve(Image &filter){





}

float& Image::set(int x, int y, int c){

	return data[getPos(x, y, c)];
}

float Image::operator() (float x, float y, int c) const{

	int px = int(x);
	int py = int(y);

	float r = x - px;
	float s = y - py;


	if(x < 0) r = 0;
	if(y < 0) s = 0;
	if(x >= w) r = 1;
	if(y >= h) s = 1;


	return  (1 - r)*(1 - s)*data[getPos(px, py,c)] +
			r*(1 - s)*		data[getPos(px+1, py,c)] +
			(1 - r)*s*		data[getPos(px, py+1,c)] +
			r*s*			data[getPos(px+1, py+1,c)];


}

int Image::getPos(int x, int y, int c) const{

	if(x < 0) x = 0;
	if(y < 0) y = 0;
	if(x >= w) x = w - 1;
	if(y >= h) y = h - 1;

	return d*(w*x + y) + c;
}
